"""聊天相关路由"""

import datetime
import json
import uuid
from typing import Any, Dict, Generator, List, Optional

from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException
from fastapi.responses import StreamingResponse
from loguru import logger
from pydantic import BaseModel

from backend.chat import chat_manager, message_repository, session_repository
from backend.chat.exceptions import ChatError, InvalidRequestError
from backend.common.config import config_manager
from backend.deps.auth import get_current_user
from backend.middleware.trace_middleware import get_trace_id

router = APIRouter(
    prefix=f"/{config_manager.api.version}/chat",
    tags=["聊天"],
    responses={404: {"description": "未找到"}},
)

UNOWNED_SESSION_VALUES = {"", None, "default", "anonymous"}


def _error_detail(error: ChatError, override_details: Any = None) -> Dict[str, Any]:
    """Build a consistent error payload."""
    return {
        "code": getattr(error, "code", "CHAT_ERROR"),
        "message": str(error),
        "details": override_details if override_details is not None else getattr(error, "details", None),
    }


class MessageItem(BaseModel):
    role: str
    content: str


class ChatRequest(BaseModel):
    session_id: str
    message: Optional[str] = None  # 兼容单消息与多消息两种格式
    messages: Optional[List[MessageItem]] = None
    use_rag: bool = True
    use_tools: bool = True
    stream: Optional[bool] = None


class ToolCallRequest(BaseModel):
    session_id: str
    tool_call: Dict[str, Any]


@router.post("")
async def chat(
    request: ChatRequest,
    background_tasks: BackgroundTasks,
    trace_id: str = Depends(get_trace_id),
    current_user: dict = Depends(get_current_user),
):
    """聊天接口 - 统一版本前缀: /{version}/chat/"""
    log = logger.bind(trace_id=trace_id, session_id=request.session_id)
    log.info("接收到聊天请求", use_rag=request.use_rag, stream=request.stream)

    try:
        final_message: Optional[str] = request.message
        if (final_message is None or final_message == "") and request.messages:
            user_messages = [m for m in request.messages if m.role == "user"]
            selected = user_messages[-1] if user_messages else request.messages[-1]
            final_message = selected.content

        if final_message is None or final_message == "":
            raise InvalidRequestError("message 或 messages 至少提供一个，并且不能为空")

        now = datetime.datetime.now()
        user_id = current_user.get("id", "anonymous")

        try:
            existing_session = session_repository.get(request.session_id)
        except ChatError as repo_error:
            raise HTTPException(status_code=500, detail=_error_detail(repo_error))

        if existing_session:
            owner = existing_session.user_id
            if owner not in UNOWNED_SESSION_VALUES and owner != user_id:
                raise HTTPException(
                    status_code=403,
                    detail=_error_detail(InvalidRequestError("会话归属不匹配")),
                )
        else:
            title = final_message[:30] + "..." if len(final_message) > 30 else final_message
            session_repository.create(request.session_id, user_id, title, now)
            session_repository.assign_messages_user(request.session_id, user_id)

        user_message_saved = False

        def persist_user_message():
            nonlocal user_message_saved
            if user_message_saved:
                return
            message_repository.add_message(
                session_id=request.session_id,
                role="user",
                content=final_message,
                metadata={"trace_id": trace_id},
                user_id=user_id,
            )
            user_message_saved = True

        if request.stream:

            def event_generator() -> Generator[str, None, None]:
                try:
                    persist_user_message()

                    full_response = ""
                    assistant_message_id = str(uuid.uuid4())

                    for chunk in chat_manager.process_message(
                        request.session_id,
                        final_message,
                        request.use_rag,
                        request.use_tools,
                        stream=True,
                    ):
                        full_response += chunk
                        event_data = json.dumps(
                            {
                                "type": "chunk",
                                "content": chunk,
                                "trace_id": trace_id,
                                "message_id": assistant_message_id,
                            },
                            ensure_ascii=False,
                        )
                        yield f"data: {event_data}\n\n"

                    try:
                        if full_response:
                            message_repository.add_message(
                                session_id=request.session_id,
                                role="assistant",
                                content=full_response,
                                metadata={"trace_id": trace_id, "message_id": assistant_message_id},
                                user_id=user_id,
                            )
                    except ChatError as save_error:
                        log.error("保存助手消息失败", error=str(save_error))
                        warning_data = json.dumps(
                            {
                                "type": "warning",
                                "content": "消息保存失败，但响应已生成",
                                "trace_id": trace_id,
                                "code": save_error.code,
                            },
                            ensure_ascii=False,
                        )
                        yield f"data: {warning_data}\n\n"

                    complete_data = json.dumps(
                        {
                            "type": "done",
                            "content": "",
                            "trace_id": trace_id,
                            "message_id": assistant_message_id,
                        },
                        ensure_ascii=False,
                    )
                    yield f"data: {complete_data}\n\n"
                except ChatError as chat_error:
                    log.error("流式响应生成失败", error=str(chat_error))
                    message_repository.rollback()
                    session_repository.rollback()
                    error_data = json.dumps(
                        {
                            "type": "error",
                            "content": str(chat_error),
                            "trace_id": trace_id,
                            "code": chat_error.code,
                        },
                        ensure_ascii=False,
                    )
                    yield f"data: {error_data}\n\n"
                except Exception as exc:  # pragma: no cover - defensive fallback
                    log.error("流式响应生成失败", error=str(exc))
                    message_repository.rollback()
                    session_repository.rollback()
                    error_data = json.dumps(
                        {
                            "type": "error",
                            "content": str(exc),
                            "trace_id": trace_id,
                        },
                        ensure_ascii=False,
                    )
                    yield f"data: {error_data}\n\n"

            return StreamingResponse(event_generator(), media_type="text/event-stream")

        try:
            persist_user_message()

            response = chat_manager.process_message(
                request.session_id,
                final_message,
                request.use_rag,
                request.use_tools,
                stream=False,
            )

            if response:
                message_repository.add_message(
                    session_id=request.session_id,
                    role="assistant",
                    content=response,
                    metadata={"trace_id": trace_id},
                    user_id=user_id,
                )

            return {"type": "response", "content": response, "trace_id": trace_id}
        except ChatError as chat_error:
            log.error("非流式聊天处理失败", error=str(chat_error))
            message_repository.rollback()
            session_repository.rollback()
            raise HTTPException(status_code=500, detail=_error_detail(chat_error))
        except Exception as exc:  # pragma: no cover - defensive fallback
            log.error("非流式聊天处理失败", error=str(exc))
            message_repository.rollback()
            session_repository.rollback()
            raise HTTPException(status_code=500, detail=f"Chat processing failed: {exc}")
    except ChatError as chat_error:
        log.error("聊天处理失败", error=str(chat_error))
        status_code = 400 if isinstance(chat_error, InvalidRequestError) else 500
        raise HTTPException(status_code=status_code, detail=_error_detail(chat_error))
    except Exception as exc:  # pragma: no cover - defensive fallback
        log.error("聊天处理失败", error=str(exc))
        raise HTTPException(status_code=500, detail=f"Chat processing failed: {exc}")


@router.post("/tools/call")
async def call_tool(request: ToolCallRequest, trace_id: str = Depends(get_trace_id)):
    """处理工具调用请求"""
    log = logger.bind(trace_id=trace_id, session_id=request.session_id)
    log.info("接收到工具调用请求")

    try:
        tool_call_json = json.dumps(request.tool_call)
        result = chat_manager.handle_tool_call(request.session_id, tool_call_json)

        if isinstance(result, dict):
            result["trace_id"] = trace_id

        return result
    except ChatError as chat_error:
        log.error("工具调用失败", error=str(chat_error))
        raise HTTPException(status_code=400, detail=_error_detail(chat_error))
    except Exception as exc:  # pragma: no cover - defensive fallback
        log.error("工具调用失败", error=str(exc))
        raise HTTPException(status_code=500, detail=f"Tool call failed: {exc}")
